<?php

//	ThinMVC v.1
//	http://code.google.com/p/phpthinmvc/
//
//	Copyright 2009 Site Solutions, Inc.
//
//	Licensed under the Apache License, Version 2.0 (the "License");
//	you may not use this file except in compliance with the License.
//	You may obtain a copy of the License at
//
//		 http://www.apache.org/licenses/LICENSE-2.0
//
//	Unless required by applicable law or agreed to in writing, software
//	distributed under the License is distributed on an "AS IS" BASIS,
//	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//	See the License for the specific language governing permissions and
//	limitations under the License.


require_once("ThinMVC_Controller.php");

class ThinMVC
{
	function ThinMVC( &$config, $dbh )
	{
		$this->config=&$config;
		$this->dbh=&$dbh;
		$this->nowrunning='';

		// Default view is ('pages')
		if (!($this->config['viewchain'])) {
			$this->config['viewchain']=array('pages');
		}

		$this->_init();
	}

	///////////////
	// Default empty init
	///////////////
	function _init() {
	}

	///////////////
	// Handle a request
	///////////////

	function handleRequest( $server, $request, $request_to_handle = '' )
	{
		$this->_logHitStart();

		// Turn on output buffering to enable redirects
		ob_start();

		if (!(is_array($server))) { $server=$_SERVER; }
		if (!(is_array($request))) { $request=$_REQUEST; }

		if (strlen($request_to_handle)>0) {
			$this->info['requestpath']=$request_to_handle;
		} else {
			// Figure it out as though the reqest is for the root.php page
			$this->info['requestpath']=substr($server['PHP_SELF'],strlen($server['SCRIPT_NAME']));
		}

		$this->info['requestpath']=preg_replace("/[^\w\.\-\/]/","",$this->info['requestpath']);
		$this->info['requestpath']=preg_replace("/^\//","",$this->info['requestpath']);

		$this->info['requeststeps']=explode("/",$this->info['requestpath']);

		$this->info['atPath']='';
		$this->info['objectParts']=array();

		$this->info['nextSteps']=$this->info['requeststeps'];

		$this->callNext();

		$content=ob_get_contents();

		ob_end_clean();

		$matches=array();

		// Use the last declared title
		$title='';
		if (preg_match("/^.*<title>(.*?)<\/title>/si",$content,$matches)) {
			$title=$matches[1];
			$content=preg_replace("/^(.*?)<title>.*?<\/title>/si","$1<REALTITLE>",$content);
			$content=preg_replace("/<title>.*?<\/title>/","",$content);
			$content=preg_replace("/<REALTITLE>/","<title>$title</title>",$content);
		}
		$pre_head='';
		$header='';
		$header_count=0;

		// header stuff
		if (preg_match("/<head/",$content)) {

			// Copy the header stuff to the one main header
			while (preg_match("/^.*?<head>(.*?)<\/head>/si",$content,$matches)) {
				$header=$header.$matches[1];
				$header_count++;
				$content=preg_replace("/^(.*?)<head>.*?<\/head>/si","$1<HEADER $header_count>",$content);
			}

			if ($header_count) {
				$content=preg_replace("/<HEADER 1>/","<REALHEADER>",$content);
				$content=preg_replace("/<HEADER \d+>/","",$content);
				$content=preg_replace("/<REALHEADER>/","<head>$header</head>",$content);
			}

			$pre_head=preg_replace("/^(.*?)(\<head.*$)/is","$1",$content);
			$content=preg_replace("/^.*?(\<head.*$)/is","$1",$content);
		}

		// Body tag?
		if (preg_match("/^.*?<body.*?>/si",$content)) {
			$content=preg_replace("/^(.*?)<body>/si","$1<REALBODY>",$content);
			$content=preg_replace("/<\\/*body.*?>/si","",$content);
			$content=preg_replace("/<\\/*html.*?>/si","",$content);
			$content=$pre_head.$content."</body></html>";
			$content=preg_replace("/<REALBODY>/","<body>",$content);
		}

		$this->preContentDisplay( $content );	

		print $content;

		$this->_logHitEnd( $content );

	}


	///////////////
	// Call the next function down the chain
	///////////////

	function callNext ( )
	{

		$this->info['subPath']='';
		$this->pathInfo='';

		// If we're under the always function, see if there is a 
		// function with the real name to run, or a default.

		if ($this->nowrunning=='always') {

			$this->nowrunning='';
			$this->info['subPath']=$this->info['nextSteps'][0];

			if ($this->info['subPath'] && method_exists($this->info["controller"],$this->info['subPath'])) {
				eval('$this->info["controller"]->'.$this->info['subPath'].'();');
				return;

			} else if (method_exists($this->info["controller"],"default_method")) {
				eval('$this->info["controller"]->default_method();');
				return;

			}
		}

		while ($objectName=array_shift($this->info['nextSteps'])) {

			$this->info['atPath']=$this->info['atPath']."/".$objectName;
			array_push($this->info['objectParts'],$objectName);

			$this->info['thisPath']=$this->config['root'].$this->info['atPath'];

			if(count($this->info['nextSteps'])>0) {
				$this->info['subPath']=$this->info['nextSteps'][0];
			} else {
				$this->info['subPath']='';
			}

			// Does the directory exist?
			if (file_exists($this->info['thisPath'])) {

				// Run the controller and call the method if it exists
				if (file_exists( $this->info['thisPath']."/".$objectName."Controller.php")) {
					// Load the controller
					include_once($this->info['thisPath']."/".$objectName."Controller.php");

					// Load the object
					$this->object='';
					if (file_exists($this->info['thisPath']."/".$objectName.".php")) {
						
						include_once($this->info['thisPath']."/".$objectName.".php");
						eval ('$this->object=new '.implode('_',$this->info['objectParts']).'($this);');
					}

					// Figure out the object name
					$this->objectPHPName=$objectName;

					eval('$this->info["controller"]=new '.implode('_',$this->info['objectParts']).'Controller($this,$this->object, $this->info["nextSteps"]);');

					// Run the always() function if it exists
					if (method_exists($this->info["controller"],"always")) {

						$this->nowrunning='always';
						eval('$this->info["controller"]->always();');

					// Run the named sub-method if it exists
					} else if ($this->info['subPath'] && method_exists($this->info["controller"],$this->info['subPath'])) {
						eval('$this->info["controller"]->'.$this->info['subPath'].'();');

					// Run the default_method if we didn't find any others
					} else if (method_exists($this->info["controller"],"default_method")) {
						eval('$this->info["controller"]->default_method();');

					} else {

						$this->error("Page not found");

					}
					break;
				} else {
					if ($this->config['environment']=='dev') {
						$this->error("Directory not found: ".$this->info['thisPath']."/".$objectName."Controller.php");
					} else {
						$this->error("Controller not found: ".$this->info['atPath']."/".$objectName."Controller.php");
					}
					break;
				}
			} else {
				if ($this->config['environment']=='dev') {
					$this->error("Directory not found: ".$this->info['thisPath']);
				} else {
					$this->error("Directory not found: ".$this->info['atPath']);
				}
				break;
			}
		}

		// Don't extend pathInfo until all is said and done...
		if ($this->info['subPath']) { 
			$this->pathInfo="/".$this->info['subPath'].$this->pathInfo;
		}
	}

	// Methods you may want to override

	// Run at the start of a request
	function _logHitStart() {
	}

	// Run at the end of a request
	// $content contains the full page content
	function _logHitEnd( &$content ) {
	}

	// Called when we run into an error
	function error( $error )
	{
		print $error;
	}

	// Do anything necessary with the finished page content here--
	// this will modify it before it is sent to the end user & logged
	function preContentDisplay( &$content ) {
	}
}

?>
